home *** CD-ROM | disk | FTP | other *** search
- /*-------------------------------------------------------------*
- | File: TREE.c - After Tomas Rokicki, Fred Fish Disk nr. 306 |
- | Revised: Maurizio LORETI (MLO) - First version 1.00, 911123 |
- | Last revision: version 1.04, MLO 920405 |
- +-------------------------------------------------------------+
- | For the description, refer to the README file, or to the |
- | Usage() procedure at the end of this program; a short help |
- | will be printed giving the command TREE ? , TREE -H or TREE |
- | followed by an invalid option. |
- *-------------------------------------------------------------*/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <exec/types.h>
- #include <exec/exec.h>
- #include <libraries/dos.h>
- #include <proto/exec.h>
- #include <proto/dos.h>
- #include "pattern.h"
-
- #define SYS_NORMAL_CODE 0 /* Exit codes for the operating system */
- #define SYS_ABORT_CODE 1
-
- #define MATCH_STRING -1 /* Flag for the NamEntry tree */
-
- #define FILENAME_MAX 128 /* Dimension of the file name buffer */
- #define OUTLINE_DIM 128 /* Dimension of the printout buffer*/
-
- /**
- | Structures to implement: a linked list of character strings
- | (StringSet); a linked list of directory entries (DirEntry);
- | and a linked list of name patterns (NamEntry).
- **/
-
- typedef struct sStringSet {
- struct sStringSet *next;
- char what[1];
- } StringSet;
-
- typedef struct sDirEntry {
- struct sDirEntry *next;
- char name[1];
- } DirEntry;
-
- typedef struct sNamEntry {
- struct sNamEntry *next;
- LONG type;
- void *pArg;
- } NamEntry;
-
- /**
- | Global variables
- **/
-
- struct Library *PatternBase = NULL; /* pattern.library pointer */
-
- struct FileInfoBlock *pFIB = NULL; /* FileInfoBlock buffer */
- int aborted = FALSE; /* CTRL-C flag */
- int dirWanted = FALSE; /* "-x" or "+x" flag */
- int namWanted = FALSE; /* "-n|N" or "+n|N" flag */
- int caseSignificant; /* "case is significant" flag */
- int fullDirPath; /* "-d" or "-D" flag */
- StringSet *dirList = NULL; /* Subdirectories tree root */
- StringSet *extList = NULL; /* Extensions tree root */
- NamEntry *namList = NULL; /* Name patterns tree root */
- char *mFormat = "%p%s"; /* Default output format */
- char outLine[OUTLINE_DIM]; /* Output print buffer */
-
- /**
- | Procedure prototypes
- **/
-
- StringSet *Add(char *s, StringSet *pSS);
- NamEntry *AddNam(char *s, NamEntry *pNE);
- void CheckControlC(void);
- int CheckExt(char *s);
- void Cleanup(int code);
- int CXBRK(void);
- int FileDate(char *pc, struct DateStamp *pDS);
- int FileTime(char *pc, struct DateStamp *pDS);
- int Member(char *s, StringSet *pSS);
- int NamMatch(char *name);
- void NoMem(void);
- void PrintFile(char *s);
- char *StrLast(char *s, char c);
- void Tree(char *s);
- void Usage(void);
-
- void main(
- int argc,
- char *argv[]
- ){
- char path[FILENAME_MAX] = "";
- register char option = 'F'; /* Default opt for first arg: format */
-
- /**
- | Main program: allocates the internal buffer for the directory
- | chain (pFIB); then scans the input line arguments; then calls
- | Tree() that does the work.
- **/
-
- if ((pFIB = (struct FileInfoBlock *)
- AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
- NoMem();
- }
-
- while (--argc > 0) {
- switch ((*++argv)[0]) {
- case '-':
- switch (option = (*argv)[1]) {
- case 'X': case 'x':
- dirWanted = FALSE;
- break;
- case 'N':
- caseSignificant = TRUE;
- namWanted = FALSE;
- break;
- case 'n':
- caseSignificant = FALSE;
- namWanted = FALSE;
- break;
- case 'D': case 'd':
- case 'F': case 'f':
- case 'R': case 'r':
- break;
- default:
- Usage();
- break;
- }
- break;
- case '+':
- switch (option = (*argv)[1]) {
- case 'X': case 'x':
- dirWanted = TRUE;
- break;
- case 'N':
- caseSignificant = TRUE;
- namWanted = TRUE;
- break;
- case 'n':
- caseSignificant = FALSE;
- namWanted = TRUE;
- break;
- default:
- Usage();
- break;
- }
- break;
- case '?':
- Usage();
- break;
- default:
- switch (option) {
- case 'd':
- fullDirPath = FALSE;
- dirList = Add(*argv, dirList);
- break;
- case 'D':
- fullDirPath = TRUE;
- dirList = Add(*argv, dirList);
- break;
- case 'x': case 'X':
- extList = Add(*argv, extList);
- break;
- case 'f': case 'F':
- mFormat = *argv;
- break;
- case 'r': case 'R':
- strcpy(path, *argv);
- option = '\0';
- break;
- case 'N': case 'n':
- namList = AddNam(*argv, namList);
- break;
- default: /* Cannot happen */
- break;
- }
- break;
- }
- }
-
- Tree(path);
-
- Cleanup(SYS_NORMAL_CODE);
- }
-
- StringSet *Add(
- char *s,
- StringSet *pSS
- ){
- register StringSet *t;
-
- /**
- | Adds the string "s" in the linked list with root "pSS".
- **/
-
- if ((t = malloc(sizeof(StringSet) + strlen(s))) == NULL) NoMem();
- t->next = pSS;
- strcpy(t->what, s);
-
- return t;
- }
-
- NamEntry *AddNam(
- char *s,
- NamEntry *pNE
- ){
- register NamEntry *t;
-
- /**
- | Adds the name pattern "s" in the linked list with root "pNE".
- | The pattern.library is opened at the first entry; and some memory
- | for the new structure is obtained from the heap. If "s" is a real
- | pattern, its "pArg" member is NULL and its "type" member is the
- | PatternHandle (must be positive); if "s" is a simple string,
- | "pArg" points to a cleaned copy of "s" and "type" is negative.
- **/
-
- if (PatternBase == NULL &&
- (PatternBase = OpenLibrary(PATLIB_NAME, PATLIB_MIN_VERSION)) == NULL) {
- fprintf(stderr, "TREE: couldn't open %s rev. %d\n",
- PATLIB_NAME, PATLIB_MIN_VERSION);
- Cleanup(SYS_ABORT_CODE);
- }
-
- if ((t = malloc(sizeof(NamEntry))) == NULL) NoMem();
-
- if (IsPattern(s, outLine, 0)) {
- t->pArg = NULL;
- if ( (t->type = caseSignificant ? AllocPattern(s, 0) :
- AllocPatternNoCase(s, 0)) < 0) {
- fprintf(stderr, "TREE: %s\n", PatternErrorString(t->type,
- "default", outLine, OUTLINE_DIM));
- Cleanup(SYS_ABORT_CODE);
- }
- } else {
- t->type = MATCH_STRING;
- if ((t->pArg = malloc(strlen(outLine) + 1)) == NULL) {
- free(t);
- NoMem();
- }
- strcpy(t->pArg, outLine);
- }
-
- t->next = pNE;
- return t;
- }
-
- void CheckControlC(void)
- {
- if ((SetSignal(0L, SIGBREAKF_CTRL_C)) & SIGBREAKF_CTRL_C) {
- aborted = TRUE;
- }
- }
-
- int CheckExt(
- char *s
- ){
- register char *pc;
-
- /**
- | Looks for the extension of the filename "s" (the characters after
- | the last '.') and compares the extension with the linked list
- | starting at "extList").
- **/
-
- return ((pc = StrLast(s, '.')) == NULL || !Member(++pc, extList));
- }
-
- void Cleanup(
- int code
- ){
- register StringSet *pS;
- register NamEntry *pNE;
-
- /**
- | Frees allocated memory, gives back all obtained resources.
- **/
-
- if (pFIB != NULL) FreeMem(pFIB, sizeof(struct FileInfoBlock));
-
- while ((pS = extList) != NULL) {
- extList = pS->next;
- free(pS);
- }
- while ((pS = dirList) != NULL) {
- dirList = pS->next;
- free(pS);
- }
- while ((pNE = namList) != NULL) {
- namList = pNE->next;
- if (pNE->pArg != NULL) free(pNE->pArg);
- if (pNE->type > 0) FreePattern(pNE->type);
- free(pNE);
- }
-
- if (PatternBase != NULL) CloseLibrary(PatternBase);
-
- exit(code);
- }
-
- int CXBRK(void)
- {
- aborted = TRUE;
- return 0;
- }
-
- int FileDate(
- char *pc,
- struct DateStamp *pDS
- ){
- register int day, month, year;
- static char *mName[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- };
-
- day = pDS->ds_Days - 2251;
- year = (4 * day + 3) / 1461;
- day -= 1461 * year / 4;
- year += 1984;
- month = (5 * day + 2) / 153;
- day = day - (153 * month + 2) / 5 + 1;
- month += 3;
- if (month > 12) {
- year++;
- month -= 12;
- }
-
- return sprintf(pc, "%02d-%s-%d", day, mName[--month], year);
- }
-
- int FileTime(
- char *pc,
- struct DateStamp *pDS
- ){
- register int hour, mins, sec;
-
- sec = pDS->ds_Tick / TICKS_PER_SECOND;
- hour = pDS->ds_Minute / 60;
- mins = pDS->ds_Minute % 60;
-
- return sprintf(pc, "%02d:%02d:%02d", hour, mins, sec);
- }
-
- int Member(
- char *s,
- StringSet *pSS
- ){
-
- /**
- | Looks for the string "s" in the linked list with root "pSS".
- | The comparison is case insensitive (stricmp).
- **/
-
- if (*s) {
- while (pSS != NULL) {
- if (!stricmp(s, pSS->what)) return TRUE;
- pSS = pSS->next;
- }
- }
-
- return FALSE;
- }
-
- int NamMatch(
- char *name
- ){
- register NamEntry *pNE = namList;
-
- /**
- | Compares the file name ("name") with the known name patterns.
- **/
-
- while (pNE != NULL) {
- if (pNE->type == MATCH_STRING) {
- if (caseSignificant) {
- if (!strcmp(name, pNE->pArg)) return TRUE;
- } else {
- if (!stricmp(name, pNE->pArg)) return TRUE;
- }
- } else {
- register LONG r = MatchThePattern(pNE->type, name);
-
- if (r < 0) {
- fprintf(stderr, "TREE: %s\n",
- PatternErrorString(r, "default", outLine, OUTLINE_DIM));
- }
- if (r) return TRUE;
- }
- pNE = pNE->next;
- }
-
- return FALSE;
- }
-
- void NoMem(void)
- {
- fputs("TREE: couldn't obtain memory\n", stderr);
- Cleanup(SYS_ABORT_CODE);
- }
-
- void PrintFile(
- char *s
- ){
- register char *p = outLine;
- register char *q = mFormat;
-
- /**
- | Prints the file name ("s") in the needed format.
- **/
-
- while (*q) {
- if (*q == '%') {
- switch (*++q) {
- case 's':
- { register char *pc;
-
- if ((pc = StrLast(s, '/')) != NULL ||
- (pc = StrLast(s, ':')) != NULL) {
- strcpy(p, ++pc);
- } else {
- strcpy(p, s);
- }
- p += strlen(p);
- ++q;
- }
- break;
- case 'p':
- { register char *pc;
- register char *ps=s;
-
- if ((pc = StrLast(s, '/')) != NULL ||
- (pc = StrLast(s, ':')) != NULL) {
- while (ps <= pc) *p++ = *ps++;
- }
- }
- ++q;
- break;
- case 'b':
- { register int i = sprintf(p, "%ld", pFIB->fib_Size);
-
- p += i;
- }
- ++q;
- break;
- case 'd':
- p += FileDate(p, &pFIB->fib_Date);
- ++q;
- break;
- case 't':
- p += FileTime(p, &pFIB->fib_Date);
- ++q;
- break;
- case 'q':
- *p++ = '\"';
- ++q;
- break;
- default:
- *p++ = *q++;
- break;
- }
- } else {
- *p++ = *q++;
- }
- }
- *p = '\0';
- puts(outLine);
- }
-
- char *StrLast(
- char *s,
- char c
- ){
- register char *pc = NULL;
-
- /**
- | Returns a pointer to the last occurrence of "c" in the
- | string "s", or NULL.
- **/
-
- while (*s) {
- if (*s == c) pc = s;
- s++;
- }
-
- return pc;
- }
-
- void Tree(
- char *path
- ){
- BPTR dlock;
- char fileName[FILENAME_MAX];
- register char *pc, *pDir;
- register DirEntry *rdE = NULL;
- register DirEntry *pdE;
-
- /**
- | This procedure checks (recursively) a directory.
- | "path" contains the full directory name; Tree() scans the wanted
- | directory, processing immediately the 'true' files; the local
- | subdirectories (if any) are checked recursively at the end.
- | If an error is detected from Tree(), the directory/file test is
- | stopped and the global flag "aborted" is set; this makes possible, in
- | the further steps, to recursively free() all the memory that has been
- | allocated in order to store subdirectory names.
- |
- | First: obtain a lock on the wanted directory, and Examine() the lock;
- | since only one directory is being scanned at a time, we can use a
- | single FileInfoBlock buffer.
- **/
-
- if ((dlock = Lock(path, ACCESS_READ)) == NULL) return;
-
- if (Examine(dlock, pFIB)) {
-
- /**
- | Prepare in "fileName" the full directory name - to which local
- | filenames will be appended; and pDir, the pointer to the directory
- | name according to the fullDirPath global flag.
- **/
-
- pc = strcpy(fileName, path);
- pc += strlen(fileName);
- if (pc != fileName && *(pc-1) != ':') *pc++ = '/';
-
- pDir = fullDirPath ? fileName : pc;
-
- /**
- | Now, loop over all directory entries. As already said, all the
- | 'real' files are immediately checked; the subdirectory names are
- | stored in a linked list to be examined at the end. This list is
- | implemented as a LIFO tree (the simplest type).
- **/
-
- while (!aborted && ExNext(dlock, pFIB)) {
- (void) strcpy(pc, pFIB->fib_FileName);
- if (pFIB->fib_DirEntryType < 0) {
-
- /**
- | A file. If a name pattern has been given, check this first;
- | then check the extension, if any.
- **/
-
- if (namWanted ^ NamMatch(pc)) continue;
- if (dirWanted ^ CheckExt(pc)) PrintFile(fileName);
- } else {
-
- /**
- | If a memory allocation error is detected when asking space for
- | our linked list, we exit the "while" loop; setting the "aborted"
- | flag before exiting, ensures that all the memory we had from
- | these malloc()'s will later be free()-ed recursively.
- **/
-
- if (!Member(pDir, dirList)) {
- if ((pdE = malloc(sizeof(DirEntry) + strlen(fileName)))
- == NULL) {
- aborted = TRUE;
- break;
- }
-
- (void) strcpy(pdE->name, fileName);
- pdE->next = rdE;
- rdE = pdE;
- }
- }
-
- CheckControlC();
- }
- }
- UnLock(dlock);
-
- /**
- | Now, loop over all detected subdirectories (if any);
- | freeing in the same time the memory used to store their names.
- **/
-
- while (rdE != NULL) {
- if (!aborted) Tree(rdE->name);
-
- free(rdE->name);
- pdE = rdE->next;
- free(rdE);
- rdE = pdE;
- }
- }
-
- void Usage(void) {
- char *desc[] = {
- "\nSyntax:\tTREE [opt arg [arg [ ... ]] [opt arg [arg [ ... ]] ...",
- "Lists on stdout the file names in the directory tree. The options are:\n",
- " -r name : starting directory (default: current working directory).",
- " -d name [name [ ... ]] : name(s) (case not significant) of the",
- " directories to be excluded (directory name only, e.g. \"pk\").",
- " -D name [name [ ... ]] : name(s) (case not significant) of the",
- " directories to be excluded (full path, e.g. \"work:tex/pk\").",
- " -n pat [pat [ ... ]] : patterns matching files to be excluded; *OR*,",
- " +n pat [pat [ ... ]] : patterns matching the only files to be",
- " listed. If given uppercase (-N,+N) the case is significant.",
- " -x ext [ext [ ... ]] : extensions of files to be excluded; *OR*,",
- " +x ext [ext [ ... ]] : extensions of the only files to be listed.",
- " -f \"fmt\" : output format, between double quotes (\"). Can contain:",
- " %s - that will be substituted from the file name;",
- " %p - the file path (e.g. \"ram:\" or \"tex:pk/\");",
- " %b - the file size (in bytes);",
- " %d - the file date (e.g. \"08-May-1988\");",
- " %t - the file time (e.g. \"09:10:11\");",
- " %q - a double quote character (\");",
- " %% - the escape character % itself;",
- " all other characters will copied to stdout.\n",
- NULL
- };
- register char **ppc = desc;
-
- while (*ppc) {
- fputs(*ppc++, stderr);
- putc('\n', stderr);
- }
-
- Cleanup(SYS_NORMAL_CODE);
- }
-